package org.msb.accentis.data;

import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.persistence.Persistence;
import javax.persistence.PersistenceException;

public class DataManagerFactory {

	private static DataManagerFactory instance = new DataManagerFactory();

	private Database currentDatabase;

	private Set<IDataManagerFactoryListener> dataManagerFactoryListeners = new HashSet<IDataManagerFactoryListener>();

	public static DataManagerFactory getInstance() {
		return instance;
	}

	public Database encryptDatabase(char[] password) {
		if (null == currentDatabase)
			return null;

		currentDatabase.getDataManager().closeEntityManager();

		try {
			openDataManager(currentDatabase.getLocation(), password);
			currentDatabase.setEncrypted(true);
		} catch (SQLException ex) {
			Logger.getLogger("accentis", "org.msb.accentis.data.resources").log(Level.WARNING, "message.cantopendatabase", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
					new Object[] { currentDatabase.getLocation(), ex.getLocalizedMessage() });
		}

		return currentDatabase;
	}

	public void changeDatabasePassword(char[] oldPassword, char[] newPassword) throws SQLException {
		if (null == currentDatabase)
			return;

		((DataManager) currentDatabase.getDataManager()).runCallableStatement("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('bootPassword','" //$NON-NLS-1$
				+ String.valueOf(oldPassword) + "," + String.valueOf(newPassword) + "')"); //$NON-NLS-1$ //$NON-NLS-2$
	}

	public Database createDataManager(String location, char[] password) {
		Map<String, String> properties = new HashMap<String, String>();
		properties.put("openjpa.ConnectionURL", createConnectionURL(location, true, password)); //$NON-NLS-1$

		return changeCurrentDatabase(new Database(new DataManager(Persistence.createEntityManagerFactory("accentis", properties)), null != password, location)); //$NON-NLS-1$
	}

	public Database openDataManager(String location, char[] password) throws SQLException {
		Map<String, String> properties = new HashMap<String, String>();
		properties.put("openjpa.ConnectionURL", createConnectionURL(location, false, password)); //$NON-NLS-1$

		Database database = new Database(new DataManager(Persistence.createEntityManagerFactory("accentis", properties)), null != password, location); //$NON-NLS-1$
		try {
			database.getDataManager().initialize();
			database.setEncrypted(null != password);
		} catch (PersistenceException ex) {
			if (ex.getCause() instanceof SQLException)
				throw (SQLException) ex.getCause();
		}

		return changeCurrentDatabase(database);
	}

	public void closeDataManager() {
		changeCurrentDatabase(null);
	}

	private static String createConnectionURL(String location, boolean create, char[] password) {
		return "jdbc:derby:" + location + "/.db" + (create ? ";create=true" : new String()) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
				+ (null != password ? ";dataEncryption=true;bootPassword=" + String.valueOf(password) : new String()); //$NON-NLS-1$
	}

	private Database changeCurrentDatabase(Database database) {
		currentDatabase = database;

		for (IDataManagerFactoryListener listener : getDataManagerFactoryListeners())
			listener.databaseChanged(currentDatabase);

		return currentDatabase;
	}

	public Database getDatabase() {
		return currentDatabase;
	}

	public void addDataManagerFactoryListener(IDataManagerFactoryListener listener) {
		getDataManagerFactoryListeners().add(listener);
	}

	public void removeDataManagerFactoryListener(IDataManagerFactoryListener listener) {
		getDataManagerFactoryListeners().remove(listener);
	}

	Collection<IDataManagerFactoryListener> getDataManagerFactoryListeners() {
		return dataManagerFactoryListeners;
	}
}
